#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include "l0001-0/l0001-0.h"
#include "l0002-0/l0002-0.h"
#include "l0031-0.h"

#define MODNAME		"[MON]"
#define L0031_0_HEARTBEAT_MESSAGE       "1"
#define DEAMON_MONITOR_HEARTBEAT_INTERVAL_SECONDS       10

int l0031_0_heartbeat(struct l0031_0_subprocst *subproc) {
    int retval = 0;
    // ERRSYS_INFOPRINT("Feed heartbeat\n");
    retval = write(subproc->pipefds[1], L0031_0_HEARTBEAT_MESSAGE, strlen(L0031_0_HEARTBEAT_MESSAGE));
    if (retval == strlen(L0031_0_HEARTBEAT_MESSAGE)) {
        return 0;
    }
    ERRSYS_FATALPRINT("Fail to write heartbeat:%s\n", strerror(errno));
    return retval;
}

int l0031_0_readheartbeat(struct l0031_0_subprocst *subproc) {
    char buf[64] = {0};
    int nread = read(subproc->pipefds[0], buf, sizeof(buf));
    // if (nread > 0) {
    //     ERRSYS_INFOPRINT("Read hearbeat %s\n", buf);
    // }
    if (nread == strlen(L0031_0_HEARTBEAT_MESSAGE)) {
        return 0;
    }
    ERRSYS_FATALPRINT("Fail to read heartbeat:(nread:%d, errno:%d)%s\n", nread, errno, strerror(errno));
    return -1;
}


struct l0031_0st* l0031_0_monitor_initialize(int nsubprocess, int heartbeat_timeout_s) {
    struct l0031_0st *l31 = zmalloc(sizeof(struct l0031_0st) + sizeof(struct l0031_0_subprocst) * nsubprocess);
    int i;
    l31->heartbeat_timeout_s = heartbeat_timeout_s;
    l31->nsubprocess = nsubprocess;
    for (i = 0;i < l31->nsubprocess;i ++) {
        l31->subprocs[i].index = i;
    }

    return l31;
}

void l0031_0_monitor_release(struct l0031_0st* l31) {
    if (l31 != NULL) {
        l31->flags |= L0031_0_FLAGS_QUIT;
    }
}

void kill_subprocess(struct l0031_0_subprocst *subproc) {
    if (subproc->pid != 0) {
        if (subproc->pipefds[0] != -1) {
            close(subproc->pipefds[0]);
            subproc->pipefds[0] = -1;
        }
        if (subproc->pipefds[1] != -1) {
            close(subproc->pipefds[1]);
            subproc->pipefds[0] = -1;
        }
        kill(subproc->pid, SIGKILL);
        waitpid(subproc->pid, NULL, 0);
    }
}

pid_t spawn_subprocess(struct l0031_0_subprocst *subproc, process_main_proc_t main_entry, void* params) {
    pid_t pid;
    if (pipe(subproc->pipefds) == -1) {
        ERRSYS_FATALPRINT("create pipe error:%s\n", strerror(errno));
        return -1;
    }

    ERRSYS_INFOPRINT("create process PIPE fd %d %d\n", subproc->pipefds[0], subproc->pipefds[1]);

    pid = fork();
    if (pid == -1) {
        ERRSYS_FATALPRINT("fork process for service error:%s\n", strerror(errno));
        return -1;
    } else if (pid == 0) {
        // 子进程执行逻辑
        subproc->pid = getpid();
        ERRSYS_INFOPRINT("subproc forked(pid:%d)\n", (int)subproc->pid);
        close(subproc->pipefds[0]);       // close subproc read
        subproc->pipefds[0] = -1;
        // ERRSYS_INFOPRINT("param %p, index %d\n", params, subproc->index);
#ifdef __TEST__
        while (true) {
            l0031_0_heartbeat(subproc);
            sleep(5);
        }
#else
        main_entry(subproc, params);   //main_entry should heartbeat inside
#endif
        exit(0);
    } else {
        // 父进程监控子进程状态
        subproc->pid = pid;
        ERRSYS_INFOPRINT("parentproc forked subproc#%d(pid:%d)\n", subproc->index, (int)pid);
        close(subproc->pipefds[1]);   //close parent write
        subproc->pipefds[1] = -1;
    }
    return pid;
}

pid_t killandspawn(struct l0031_0_subprocst *subproc, process_main_proc_t main_entry, void* params) {
    if (subproc->pid != 0) {
        //kill and respawn
        ERRSYS_FATALPRINT("Kill and spawn subproc#%d(pid:%d)\n", subproc->index, subproc->pid);
        kill_subprocess(subproc);
        sleep(1);
    }
    return spawn_subprocess(subproc, main_entry, params);
}

int get_max_fd(fd_set fds, int max_fd) {
    for (int fd = max_fd; fd >= 0; fd--) {
        if (FD_ISSET(fd, &fds)) {
            return fd;
        }
    }
    return -1;  // 没有找到被设置的文件描述符
}

int l0031_0_monitor_start(struct l0031_0st *l31, process_main_proc_t main_entry, void* params) {
    
    int retval = -1;
    int index = 0;
    struct timespec ts_now;
    sigset_t mask;
    fd_set fds;
    FD_ZERO(&fds);
    struct timespec ts_timeout;
    int maxfd = -1;

    ts_timeout.tv_sec = 1;  // 设置2秒检查一次
    ts_timeout.tv_nsec = 0;
    if (ts_timeout.tv_sec == 0) {
        ts_timeout.tv_sec = 1;
    }

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);  // 将 SIGINT 信号添加到信号屏蔽字中，屏蔽该信号

    if (l31->heartbeat_timeout_s < DEAMON_MONITOR_HEARTBEAT_INTERVAL_SECONDS) {
        l31->heartbeat_timeout_s = DEAMON_MONITOR_HEARTBEAT_INTERVAL_SECONDS;
    }

    maxfd = get_max_fd(fds, FD_SETSIZE - 1);
    // ERRSYS_INFOPRINT("max fd = %d\n", maxfd);

    while (IS_L0031_0_QUIT(l31) == 0) {
        fd_set rset = fds;
        
        int ret = pselect(maxfd + 1, &rset, NULL, NULL, &ts_timeout, &mask);
        clock_gettime(CLOCK_MONOTONIC, &ts_now);
        // ERRSYS_FATALPRINT("pselect ret %d\n",ret);
        if (ret == -1) {
            if (errno == EINTR) {
                continue;
            }
            ERRSYS_FATALPRINT("heartbeat monitor lost, pselect: %s\n", strerror(errno));
            return -1;
        } else if (ret > 0) {
            for (index = 0; index < l31->nsubprocess; index ++) {
                struct l0031_0_subprocst *subproc = &(l31->subprocs[index]);
                if (FD_ISSET(subproc->pipefds[0], &rset)) {
                    if (l0031_0_readheartbeat(subproc) != 0) {
                        ERRSYS_FATALPRINT("read subporcess#%d error: %s\n", index, strerror(errno));
                        // reset timestamp to make it timed out early
                        memset(&subproc->heartbeat_timespec_last, 0, sizeof(struct timespec));
                        continue;
                    }
                    //update heartbeat timestamp
                    memcpy(&subproc->heartbeat_timespec_last, &ts_now, sizeof(struct timespec));
                }
            }
        }

        for (index = 0; index < l31->nsubprocess; index ++) {
            struct l0031_0_subprocst *subproc = &(l31->subprocs[index]);
            if (l0001_second_elapsed(&ts_now, &subproc->heartbeat_timespec_last) >= l31->heartbeat_timeout_s) {
                // subprocess dead? respawn the subprocess
                // ERRSYS_INFOPRINT("remove fd %d\n", subproc->pipefds[0]);
                FD_CLR(subproc->pipefds[0], &fds);
                subproc->pid = killandspawn(subproc, main_entry, params);
                // ERRSYS_INFOPRINT("add fd %d\n", subproc->pipefds[0]);
                FD_SET(subproc->pipefds[0], &fds);
            }
        }
        maxfd = get_max_fd(fds, FD_SETSIZE - 1);
        // ERRSYS_INFOPRINT("maxfd %d\n", maxfd);
        sleep(1);
    }

// err1:
    for (index = 0; index < l31->nsubprocess; index ++) {
        struct l0031_0_subprocst *subproc = &(l31->subprocs[index]);
        kill_subprocess(subproc);
    }
    return retval;
}